/**
 * SSE (Server-Sent Events) 客户端封装
 * 提供类似 MQTT 的主题订阅机制
 */

export interface SseTopicOptions {
  baseUrl?: string;
  reconnectPeriod?: number;
}

export type OnMessageFun = (topic: string, data: any) => void;

export class SseClient {
  private baseUrl: string;
  private reconnectPeriod: number;
  private eventSource: EventSource | null = null;
  private subscribedTopics: Set<string> = new Set();
  private topicCallbacks: Map<string, OnMessageFun> = new Map();
  private reconnectTimer: number | null = null;
  private isConnecting: boolean = false;

  constructor(baseUrl: string = '', options: SseTopicOptions = {}) {
    this.baseUrl = baseUrl || '';
    this.reconnectPeriod = options.reconnectPeriod || 5000;
  }

  /**
   * 判断是否已连接
   */
  public is_connect(): boolean {
    return this.eventSource !== null && this.eventSource.readyState === EventSource.OPEN;
  }

  /**
   * 订阅主题（类似 MQTT subscribe）
   * @param topics 主题数组，支持通配符（需服务端支持）
   * @param callback 消息回调函数
   */
  public subscribe(topics: string | string[], callback: OnMessageFun): void {
    const topicArray = Array.isArray(topics) ? topics : [topics];
    
    // 注册回调
    topicArray.forEach(topic => {
      this.subscribedTopics.add(topic);
      // 如果已存在回调，合并处理（或覆盖，根据需求）
      const existingCallback = this.topicCallbacks.get(topic);
      if (existingCallback) {
        // 保留原有回调，添加新回调（或根据需要替换）
        this.topicCallbacks.set(topic, callback);
      } else {
        this.topicCallbacks.set(topic, callback);
      }
    });

    // 如果已连接，重新订阅以更新服务端
    if (this.is_connect()) {
      this._reconnect();
    } else if (!this.isConnecting) {
      this.connect();
    }
  }

  /**
   * 取消订阅（类似 MQTT unsubscribe）
   */
  public unsubscribe(topics: string | string[]): void {
    const topicArray = Array.isArray(topics) ? topics : [topics];
    
    topicArray.forEach(topic => {
      this.subscribedTopics.delete(topic);
      this.topicCallbacks.delete(topic);
    });

    // 重新连接以更新服务端订阅列表
    if (this.is_connect()) {
      this._reconnect();
    }
  }

  /**
   * 设置主题消息回调（类似 MQTT message_callback）
   */
  public message_callback(topicPattern: string, callback: OnMessageFun): void {
    this.topicCallbacks.set(topicPattern, callback);
  }

  /**
   * 连接 SSE 服务
   */
  public connect(): void {
    if (this.isConnecting || this.is_connect()) {
      return;
    }

    this.isConnecting = true;

    try {
      // 构建订阅 URL
      const topics = Array.from(this.subscribedTopics);
      if (topics.length === 0) {
        console.warn('No topics subscribed, cannot connect');
        this.isConnecting = false;
        return;
      }

      // 方式1: 通过查询参数传递主题（推荐，兼容性好）
      const topicsParam = topics.join(',');
      const url = `${this.baseUrl}/sse/subscribe?topics=${encodeURIComponent(topicsParam)}`;

      // 方式2: 如果服务端支持，也可以通过路径传递
      // const url = `${this.baseUrl}/sse/subscribe/${topics.join(',')}`;

      // 关闭旧连接
      this.disconnect();

      // 创建新连接
      this.eventSource = new EventSource(url);

      this.eventSource.onopen = () => {
        console.log('SSE Connection succeeded!');
        this.isConnecting = false;
        if (this.reconnectTimer) {
          clearTimeout(this.reconnectTimer);
          this.reconnectTimer = null;
        }
      };

      // 监听默认消息事件
      this.eventSource.onmessage = (event: MessageEvent) => {
        try {
          const data = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
          
          // 如果服务端在消息中传递了 topic 信息
          const topic = data.topic || 'default';
          
          // 匹配回调（支持通配符匹配，类似 MQTT）
          this.topicCallbacks.forEach((callback, pattern) => {
            if (this._matchTopic(topic, pattern)) {
              callback(topic, data);
            }
          });
        } catch (err) {
          console.error('SSE message parse error:', err);
        }
      };

      // 监听自定义事件（服务端可以通过 event:xxx 发送）
      // 例如：服务端发送 event: message，客户端通过 addEventListener 监听
      this.eventSource.addEventListener('message', (event: any) => {
        try {
          const data = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
          const topic = data.topic || 'default';
          
          this.topicCallbacks.forEach((callback, pattern) => {
            if (this._matchTopic(topic, pattern)) {
              callback(topic, data);
            }
          });
        } catch (err) {
          console.error('SSE custom event parse error:', err);
        }
      });

      this.eventSource.onerror = (error: Event) => {
        console.error('SSE Connection error!', error);
        this.isConnecting = false;
        this._scheduleReconnect();
      };

    } catch (err) {
      console.error('SSE connect failed:', err);
      this.isConnecting = false;
      this._scheduleReconnect();
    }
  }

  /**
   * 断开连接
   */
  public disconnect(): void {
    if (this.eventSource) {
      this.eventSource.close();
      this.eventSource = null;
    }
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
      this.reconnectTimer = null;
    }
    this.isConnecting = false;
  }

  /**
   * 主题匹配（支持通配符，类似 MQTT）
   * - '#' 匹配多级（如 'frontend/#' 匹配 'frontend/device/001'）
   * - '+' 匹配单级（如 'frontend/+/001' 匹配 'frontend/device/001'）
   */
  private _matchTopic(topic: string, pattern: string): boolean {
    // 精确匹配
    if (topic === pattern) {
      return true;
    }

    // 通配符匹配
    const patternParts = pattern.split('/');
    const topicParts = topic.split('/');

    for (let i = 0; i < Math.max(patternParts.length, topicParts.length); i++) {
      const patternPart = patternParts[i];
      const topicPart = topicParts[i];

      if (patternPart === '#') {
        // '#' 必须出现在最后，匹配所有剩余层级
        return true;
      }

      if (patternPart === '+') {
        // '+' 匹配单层，继续下一层
        continue;
      }

      if (patternPart !== topicPart) {
        return false;
      }
    }

    return patternParts.length === topicParts.length;
  }

  /**
   * 重新连接
   */
  private _reconnect(): void {
    this.disconnect();
    setTimeout(() => {
      this.connect();
    }, 100);
  }

  /**
   * 安排重连
   */
  private _scheduleReconnect(): void {
    if (this.reconnectTimer) {
      return;
    }

    this.reconnectTimer = window.setTimeout(() => {
      console.log('SSE reconnecting...');
      this.reconnectTimer = null;
      if (this.subscribedTopics.size > 0) {
        this.connect();
      }
    }, this.reconnectPeriod);
  }
}

